import * as _ from 'lodash';
import { agentService } from '../model/agent/service';
import { AgentStatus } from '../model/agent/type';
import { SimJobStatus } from '../model/simulation/type';
import { eventBus } from './eventBus';
import { JobFailedEvent } from '../event';
import * as connectionManager from './connectionManager';
import { SimulationJobService } from '../model/simulation/service';
import logger from '../../common/logger';

export class JobScanService {

  private jobScanInterval: number;
  private jobTimeout: number;

  constructor(jobScanInterval: number = 60000, jobTimeout: number = 1) {
    this.jobScanInterval = jobScanInterval;
    this.jobTimeout = jobTimeout;
  }

  init() {
    setInterval(() => {
      this.scanSimJob();
    }, this.jobScanInterval);
  }

  async scanSimJob() {
    const simulationService = new SimulationJobService();
    const jobs = await simulationService.findTimeoutJobs(this.jobTimeout);
    if (_.isEmpty(jobs)) {
      return;
    }
    logger.info(`timeout jobs count: ${jobs.length}`);
    const agents = [];
    jobs.forEach(job => {
      if (!_.includes(agents, job.agentId)) {
        agents.push(job.agentId);
      }
    });

    const offlineAgents = await agentService.findByIdentitiesAndStatus(agents, AgentStatus.OFFLINE);
    const offlineAgentIds = offlineAgents.map(j => j.identity);
    const onlineAgentIds = _.difference(agents, offlineAgentIds);

    jobs.filter(job => _.includes(offlineAgentIds, job.agentId)).forEach(job => {
      logger.info(`update job(${job.id}) to failed`);
      job.status = SimJobStatus.Failed;
      if (!_.isEmpty(job.progress)) {
        job.progress = 100;
      }
      job.finishedAt = new Date();
      simulationService.save(job);
      job.message = `job timeout(agent offline and job time > ${this.jobTimeout} h), fix status to failed automation`;
      eventBus.emit(JobFailedEvent.NAME, new JobFailedEvent(job));
    });

    const agentJobs = {};
    jobs.filter(job => _.includes(onlineAgentIds, job.agentId)).forEach(job => {
      if (job.agentId in agentJobs) {
        agentJobs[job.agentId].push(`${job.id}`);
      } else {
        agentJobs[job.agentId] = [`${job.id}`];
      }
    });
    _.forIn(agentJobs, (val, key) => {
      const connection = connectionManager.getConnection(key);
      if (connection) {
        connection.emit('simulation:query', val, async (res) => {
          logger.info(`simulation query result = ${JSON.stringify(res)}`);
          _.forIn(res, async (val, key) => {
            if (val === SimJobStatus.UNKNOWN) {
              const job = await simulationService.findById(key);
              if (job) {
                job.status = SimJobStatus.Failed;
                if (!_.isEmpty(job.progress)) {
                  job.progress = 100;
                }
                job.message = 'job status lose, fix status to failed automation';
                job.finishedAt = new Date();
                await simulationService.save(job);
              }
            }
          });
        });
      }
    });

  }
}